#pragma once
#include <vector>

#include "Deck.h"
#include "Player.h"
#include "Console.h"

using namespace std;

class Game
{
public:

	enum Combination
	{
		Kicker = 0,
		Pair = 1,
		TwoPairs = 2,
		Triplet = 3,
		Straight = 4,
		Flush = 5,
		FullHouse = 6,
		Square = 7,
		StraightFlush = 8,
	};

	struct Hand
	{
		Combination comb;
		Card::CardValue kiker;
		bool operator<(Hand hand)
		{
			if (comb < hand.comb)
				return true;
			else if (comb > hand.comb)
				return false;
			else if (kiker < hand.kiker)
				return true;
			else
				return false;
		}
	};

	enum GameLevel
	{
		Preflop = 0,
		Flop = 1,
		Tern = 2,
		River = 3,
		EndLevel = 4
	};

	Game(unsigned short playersN = 3, unsigned short bet = 20)
	: playersN(playersN)
	, dillerN(0)
	, choisePlayerN(2)
	, canCheck(false)
	, bank(0)
	, betBlain(bet)
	, gameLevel(Preflop)
	{
		Player playerAI;
		for (unsigned short i = 0; i < playersN; i++)
			Players.push_back(playerAI);
		unsigned short money = 1000;
		player.SetMoney(money);
		for (unsigned short i = 0; i < playersN; i++)
			Players[i].SetMoney(money);
	};
	virtual ~Game(void){};

	void Start()
	{
		//deck.Shuffle();
	}

	Card GetFlop(unsigned short cardN)
	{
		if (cardN < 3)
			return deck.GetCard(cardN);
		else
			return Card();
	}

	string GetFlopStr()
	{
		string flop = "________";
		flop[0] = deck.GetCard(0).GetValueStr();
		flop[1] = deck.GetCard(0).GetLearStr();
		flop[2] = ' ';
		flop[3] = deck.GetCard(1).GetValueStr();
		flop[4] = deck.GetCard(1).GetLearStr();
		flop[5] = ' ';
		flop[6] = deck.GetCard(2).GetValueStr();
		flop[7] = deck.GetCard(2).GetLearStr();		
		return flop;
	}

	Card GetTurn()
	{
		return deck.GetCard(3);
	}

	string GetTurnStr()
	{
		string turn = "__";
		turn[0] = deck.GetCard(3).GetValueStr();
		turn[1] = deck.GetCard(3).GetLearStr();
		return turn;
	}

	Card GetRiver()
	{
		return deck.GetCard(4);
	}

	string GetRiverStr()
	{
		string river = "__";
		river[0] = deck.GetCard(4).GetValueStr();
		river[1] = deck.GetCard(4).GetLearStr();
		return river;
	}

	void SetAllPlayersMoney(unsigned short money)
	{
		player.SetMoney(money);
		for (unsigned short i = 0; i < playersN; i++)
			Players[i].SetMoney(money);
	}

	void SetNumAIPlayers(unsigned short playersNum)
	{
		playersN = playersNum;
		Player playerAI;
		for (unsigned short i = 0; i < playersN; i++)
			Players.push_back(playerAI);

	}

	void HandingCards()
	{
		player.SetCards(deck.GetCard(5), deck.GetCard(6));
		for (unsigned short i = 0; i < playersN; i++)		
			Players[i].SetCards(deck.GetCard(7+i), deck.GetCard(8+i));
	}

	Player GetPlayer()
	{
		return player;
	}

	Player GetPlayerAI(unsigned short i)
	{	
		if ( i < playersN)
			return Players[i];
		else
			return Player();
	}

	void ErasePalyerAI(unsigned short playerN)
	{
		this->playersN--;
		vector<Player>::iterator playerErase = Players.begin();
		for (int i = 0; i < playersN; i++)
			playerErase++;
		Players.erase(playerErase);
	}

	bool UpdateLap()
	{	   
		if (gameLevel == Preflop)
		{
			unsigned short smallBlainN = dillerN + 1;
			if (smallBlainN > playersN)
				smallBlainN -= playersN + 1;
			
			if (smallBlainN == playersN)
			{
				if (player.SubMoney(betBlain/2))
				{				
					bank += betBlain/2;
				} else
				{
					EndGame();					
				} 

			} else
			{
				if (Players[smallBlainN].SubMoney(betBlain/2))
				{				
					bank += betBlain/2;
				} else
				{
					ErasePalyerAI(smallBlainN);
					return UpdateLap();
				} 
			}

			unsigned short bigBlainN = smallBlainN + 1;
			if (bigBlainN > playersN)
				bigBlainN -= playersN + 1;

			if (bigBlainN == playersN)
			{
				if (player.SubMoney(betBlain))
				{				
					bank += betBlain;
				} else
				{
					EndGame();					
				}
			} else
			{
				if (Players[bigBlainN].SubMoney(betBlain))
				{				
					bank += betBlain;
				} else
				{
					ErasePalyerAI(bigBlainN);
					return UpdateLap();
				} 
			}

		}
		Print(gameLevel);
		if (gameLevel < EndLevel)
		{			
			if (canCheck)
			{			
				Player::PlayerChooise playerChooise = GetPlayerChooise();
			}else
			{
				Player::PlayerChooise playerChooise = GetPlayerChooise();
			}

			for(unsigned short i = 0; i < playersN; i++)
			{

			}

		} else
		{  
			console->Getch();
		}
		
		if (gameLevel < EndLevel)
		{
			gameLevel = GameLevel(unsigned short(gameLevel) + 1);
			return false;
		}
		else
		{
			EndGameLevel();
			return true;
		}		
	}

	void EndGame()
	{
		console->Clear();
		console->Print("You Lost");
		console->Print("\n");
	}

	void EndGameLevel()
	{
		unsigned short playerWin = GetPlayerNumWin();
		if (playerWin == playersN)
		{
			player.AddMoney(bank);
		} else
		{
			Players[playerWin].AddMoney(bank);
		}

		bank = 0;
		gameLevel = Preflop;
		if (dillerN >= playersN)
			dillerN = 0;
		else
			dillerN++;
	}

	unsigned short GetPlayerNumWin()
	{
		Hand maxHand = GetMaxComb(player);
		unsigned short maxHandNum = playersN;
		for (unsigned short i = 0; i < playersN; i++)
		{
			if (i < playersN)
			{
				Hand hand = GetMaxComb(Players[i]);
				if (maxHand < hand)
				{
					maxHand = hand;
					maxHandNum = i;
				}
			}
		}
		return maxHandNum;
	}

	void Print(GameLevel gameLevel = Preflop)
	{
		console->Clear();
		
		deck.Print();

		console->Print("\n");

		if (gameLevel >= Preflop)
		{			
			console->SetColor(Console::Yellow, Console::Green);
			console->Print("Player:\t\t");
			Card card1 = player.GetCard(0);
			if (card1.IsRed())
				console->SetColor(Console::Red, Console::Green);
			else
				console->SetColor(Console::Black, Console::Green);
			console->Print(card1.GetCardStr());
			console->Print(" ");

			Card card2 = player.GetCard(1);
			if (card2.IsRed())
				console->SetColor(Console::Red, Console::Green);
			else
				console->SetColor(Console::Black, Console::Green);
			console->Print(card2.GetCardStr());
			console->Print(" ");

			console->SetColor(Console::Yellow, Console::Green);
			console->Print("$: ");
			console->Print(int(player.GetMoney()));
			if (dillerN == playersN)
				console->Print("  Diller");
			console->Print("\n");

			if (gameLevel == EndLevel)
			{
				for (unsigned short i = 0; i < playersN; i++)
				{
					console->SetColor(Console::Yellow, Console::Green);
					console->Print("PlayerAI_");
					console->Print(i+1);
					console->Print(":\t");
					Card card1 = Players[i].GetCard(0);
					if (card1.IsRed())
						console->SetColor(Console::Red, Console::Green);
					else
						console->SetColor(Console::Black, Console::Green);
					console->Print(card1.GetCardStr());
					console->Print(" ");
					Card card2 = Players[i].GetCard(1);
					if (card2.IsRed())
						console->SetColor(Console::Red, Console::Green);
					else
						console->SetColor(Console::Black, Console::Green);
					console->Print(card2.GetCardStr());
					console->Print(" ");

					console->SetColor(Console::Yellow, Console::Green);
					console->Print("$: ");
					console->Print(int(Players[i].GetMoney()));
					if (i == dillerN)
						console->Print("  Diller");
					console->Print("\n");
				} 

			} else
			{
				for (unsigned short i = 0; i < playersN; i++)
				{
					console->SetColor(Console::Yellow, Console::Green);
					console->Print("PlayerAI_");
					console->Print(i+1);
					console->Print(":\t");
					console->SetColor(Console::Black, Console::Green);
					console->Print("#  #  ");
					console->SetColor(Console::Yellow, Console::Green);
					console->Print("$: ");
					console->Print(int(Players[i].GetMoney()));
					if (i == dillerN)
						console->Print("  Diller");
					console->Print("\n");
				}
			}			
		} if (gameLevel >= Flop)
		{
			string flop = GetFlopStr();
			if (flop[1] == 3 || flop[1] == 4)
				console->SetColor(Console::Red, Console::Green);
			else
				console->SetColor(Console::Black, Console::Green);
			console->Print(flop[0]);
			console->Print(flop[1]);
			console->Print(" ");

			if (flop[4] == 3 || flop[4] == 4)
				console->SetColor(Console::Red, Console::Green);
			else
				console->SetColor(Console::Black, Console::Green);
			console->Print(flop[3]);
			console->Print(flop[4]);
			console->Print(" ");

			if (flop[7] == 3 || flop[7] == 4)
				console->SetColor(Console::Red, Console::Green);
			else
				console->SetColor(Console::Black, Console::Green);
			console->Print(flop[6]);
			console->Print(flop[7]);
			console->Print(" ");

		} else if (gameLevel != EndLevel)
		{
			console->Print("\t");
		}if (gameLevel >= Tern)
		{
			string turn = GetTurnStr();
			if (turn[1] == 3 || turn[1] == 4)
				console->SetColor(Console::Red, Console::Green);
			else
				console->SetColor(Console::Black, Console::Green);
			console->Print(turn);
			console->Print(" ");
		} if (gameLevel >= River)
		{
			string river = GetRiverStr();
			if (river[1] == 3 || river[1] == 4)
				console->SetColor(Console::Red, Console::Green);
			else
				console->SetColor(Console::Black, Console::Green);
			console->Print(river);
			console->Print(" ");		
		}

		console->Print("\n");

		{				
			console->SetColor(Console::Yellow, Console::Green);
			console->Print("\tBank: ");
			console->Print(bank);
			console->Print("\n");
		}

		console->Print("\n");

		if (gameLevel == EndLevel)
		{
			console->SetColor(Console::Yellow, Console::Green);			
			console->Print("Win Player");			
		}
	}

	void SetConsole(Console * console)
	{
		this->console = console;
	}

	Player::PlayerChooise GetPlayerChooise()
	{
		console->Print("\n");
		console->SetColor(Console::Yellow, Console::Green);
		console->Print("Call: C Fold: F Check: H ?");
		char playerChoise;
		playerChoise = console->Getch();
		if (playerChoise == 'c' || playerChoise == 'C')
		{											 
			return Player::Call;
		} else if (playerChoise == 'f' || playerChoise == 'F')
		{
			return Player::Fold;
		} else if (playerChoise == 'h' || playerChoise == 'H')
		{
			return Player::Check;
		} else
			return GetPlayerChooise();
	}

	bool IsStraightFlush(Card *cards, unsigned short numCards = 7)
	{
		unsigned short count = 0;
		for (int i = 0; i < (numCards - 1); i++)
		{
			if (unsigned short (cards[i].GetValue()) - 1 == unsigned short(cards[i+1].GetValue()))
			{
				count++;
				if (count == 5)
					break;
			} else
				count = 0;
		}

		if (count == 5)
			return true;
		else
			return false;
	}

	bool IsSquare(Card *cards, unsigned short numCards = 7)
	{
		int count = 0;
		for (unsigned short val = unsigned short(Card::A); val >= unsigned short (Card::K2); val--)
		{
			for (int i = 0; i < (numCards - 1); i++)
			{
				if (unsigned short(cards[i].GetValue()) == val)
					count++;
			}
			if (count == 4)
				break;
		}
		if (count == 4)
			return true;
		else
			return false;
	}
	Hand GetMaxComb(Player player)
	{  
		Card *cardPlayer = player.GetCards();
		Card *fiveCards = GetFiveCards();

		Card sevenCards[7];
		sevenCards[0] = cardPlayer[0];
		sevenCards[1] = cardPlayer[1];
		sevenCards[2] = fiveCards[0];
		sevenCards[3] = fiveCards[1];
		sevenCards[4] = fiveCards[2];
		sevenCards[5] = fiveCards[3];
		sevenCards[6] = fiveCards[4];

		for (int i = 0; i < (7 - 1); i++)
			for (int j = (i+1); j < 7; j++)
				if (sevenCards[i].GetValue() < sevenCards[j].GetValue() ||
					(sevenCards[i].GetValue() == sevenCards[j].GetValue() &&
					sevenCards[i].GetLear() < sevenCards[j].GetLear()))
				{
					Card temp = sevenCards[i];
					sevenCards[i] = sevenCards[j];
					sevenCards[j] = temp;
				} 

		Hand hand;
		//hand.comb = StraightFlush;
		//hand.kiker = Card::A;		

		if (IsStraightFlush(sevenCards))
		{
			
		} else if (IsSquare(sevenCards))
		{
			
		}

		return hand;
	}

	Card *GetFiveCards()
	{
		//Card cards[5] = {Card(), Card(), Card(), Card(), Card()};
		Card *cards = new Card[5];
		for (unsigned short i = 0; i < 5; i++)
		{
			Card card = deck.GetCard(i);
			cards[i].SetCard(card);
		}
		return cards;
	}
	


private:
	Deck deck;
	unsigned short playersN;
	Player player;
	vector<Player> Players;
	unsigned short dillerN;
	unsigned short choisePlayerN;
	unsigned short bank;
	unsigned short betBlain;
	unsigned short lastWinNum;
	GameLevel gameLevel;
	bool canCheck;	
	Console *console;
};